<?php

abstract class Soul_Model_Entity_Version extends Soul_Model_Entity_Base{

    protected $versionTableName = '';
    protected $versionProperties = array();
    protected $mainProperties = array();
    protected $versionIdProperty = 'version_id';
    protected $versionRelationProperty = 'main_id';
    
    public function __construct() {
        parent::__construct();
    }    
    
    protected function initialize() {
        if (!isset(self::$initializedProperties[get_class($this)])) {
            $this->initializeProperties();
            $this->createProperty($this->versionRelationProperty, $this->getIdProperty()->getPropertyType())
                    ->setInsertable(TRUE)
                    ->setUpdateable(FALSE)
                    ->setVersionable(TRUE)
                    ->setInputType(NULL);
            if(!$this->hasProperty($this->versionIdProperty)){
                $this->createProperty($this->versionIdProperty, 'integer', TRUE, TRUE)
                        ->setVersionable(TRUE);                
            }
            self::$initializedProperties[get_class($this)] = $this->properties;
        }
        
        $this->properties = self::$initializedProperties[get_class($this)];
        foreach($this->getProperties() as $property){
            if(!$property->getVersionable()){
                $this->mainProperties[$property->getName()] = $property;
            }else{
                $this->versionProperties[$property->getName()] = $property;
            }
        }
        
        if(empty($this->versionTableName)){
            $this->versionTableName = $this->tableName.'_version';
        }
        $this->markNew();
    }
    
    public function getVersionTableName(){
        return $this->versionTableName;
    }
    
    public function getMainProperties(){
        return $this->mainProperties;
    }
    
    public function getVersionProperties(){
        return $this->versionProperties;
    }
    
    public function getVersionIdProperty(){
        return $this->getProperty($this->versionIdProperty);
    }
    
    public function getVersionRelationProperty(){
        return $this->getProperty($this->versionRelationProperty);
    }
    
    public function getVersionIdValue(){
        return $this->getData($this->versionIdProperty);
    }
    
    public function setVersionIdValue($value){
        $this->setData($this->versionIdProperty, $value);
    }
    
    public function setIdValue($value) {
        $this->setData($this->getIdProperty()->getName(), $value)
             ->setData($this->versionRelationProperty, $value);
        return $this;
    }    
    
    public function getQuery() {
        return parent::getQuery()
                ->from($this->getTableName(), 'm')
                ->select('`m`.*,`v`.*')
                ->joinInner($this->getVersionTableName(), 'v', 
                            'm.'.$this->getIdProperty()->getName(), 'v.'.$this->versionRelationProperty);
    }
    
    public function getVersionUpdate($changed = TRUE){
    	$result = Soul_Db_Factory::createUpdate();
    	$result->update($this->getVersionTableName());
        foreach($this->getChangedProperties() as $property){
    		if($property->getUpdateable() && $property->getVersionable()){
    			$result->set($property->getName(), 
    				$property->convertToDb($this->getData($property->getName())),
    				$property->getParameterType());
    		}
    	}
    	return $result; 
    }    
    
    public function insert() {
        $this->beforeInsert();
        $this->insertSelf();
        $this->insertVersion();
        $this->afterInsert();
        $this->markNoChanged();
        return $this;
    }    
    
    protected function insertSelf() {
        $insert = Soul_Db_Factory::createInsert()->insert($this->getTableName());
        foreach($this->getMainProperties() as $property){
            if($property->getInsertable()){
                $insert->set($property->getName(), 
                             $property->convertToDb($this->getData($property->getName())),
                             $property->getParameterType());
            }
        }
        $insert->execute($this->getDb());
    	if($this->getIdProperty()->getIsIncrement()){
    		$this->setIdValue($this->getDb()->getInsertId());
    	}
    }

    protected function insertVersion(){
        $insert = Soul_Db_Factory::createInsert()->insert($this->getVersionTableName());
        foreach($this->getVersionProperties() as $property){
            if($property->getInsertable()){
                $insert->set($property->getName(), 
                             $property->convertToDb($this->getData($property->getName())),
                             $property->getParameterType());
            }
        }
        $insert->execute($this->getDb());
    	if($this->getIdProperty()->getIsIncrement()){
            $this->setData($this->versionIdProperty, $this->getDb()->getInsertId());
    	}
    }
    
    public function update() {
        $this->beforeUpdate();
        if ($this->isChanged()) {
            $this->updateSelf();
            $this->updateVersion();
        }
        $this->afterUpdate();
        $this->markNoChanged();
        return $this;
    }    
    
    protected function updateSelf() {
        $canUpdate = FALSE;
        $update = Soul_Db_Factory::createUpdate()->update($this->getTableName());
    	foreach($this->getChangedProperties() as $property){
    		if($property->getUpdateable() && !$property->getVersionable()){
                $update->set($property->getName(), 
                             $property->convertToDb($this->getData($property->getName())), 
                             $property->getParameterType());
                $canUpdate = TRUE;
    		}
    	}
        if($canUpdate){
            $idProperty = $this->getIdProperty();
            $update->condition($idProperty->getName(), 
                               $idProperty->convertToDb($this->getIdValue()),
                               'eq',
                               $idProperty->getParameterType());
            $update->execute($this->getDb());
        }
    }

    protected function updateVersion(){       
        $canUpdate = FALSE;
        $update = Soul_Db_Factory::createUpdate()->update($this->getVersionTableName());
    	foreach($this->getChangedProperties() as $property){
    		if($property->getUpdateable() && $property->getVersionable()){
                $update->set($property->getName(), 
                             $property->convertToDb($this->getData($property->getName())), 
                             $property->getParameterType());
                $canUpdate = TRUE;
    		}
    	}
        if($canUpdate){
            $idProperty = $this->getVersionIdProperty();
            $update->condition($idProperty->getName(), 
                               $idProperty->convertToDb($this->getVersionIdValue()),
                               'eq',
                               $idProperty->getParameterType());
            $update->execute($this->getDb());
        }        
    }    
    
    public function delete() {
    	$this->beforeDelete();
        $this->deleteVersion();
        $this->deleteSelf();
        $this->afterDelete();
        return $this;
    }
    
    protected function deleteVersion(){
        $idProperty = $this->getVersionIdProperty();
        $delete = Soul_Db_Factory::createDelete()->delete($this->getVersionTableName());
        $delete->condition($idProperty->getName(), $this->getVersionIdValue(), 'eq', $idProperty->getParameterType())
               ->execute($this->getDb());  	
    }
    
    protected function deleteSelf(){
        $q = $GLOBALS[RC]->getQuery()
                ->from($this->getVersionTableName())
                ->select('COUNT(*)')
                ->condition($this->versionRelationProperty, $this->getIdValue());
        if($q->fetch()->getFirstValue() == 0){
            $idProperty = $this->getIdProperty();
            $delete = Soul_Db_Factory::createDelete()->delete($this->getTableName());
            $delete->condition($idProperty->getName(), $this->getIdValue(), 'eq', $idProperty->getParameterType())
                   ->execute($this->getDb());          
        }
    }    
    
    public function updateBatch(array $ids){
        throw new Exception('Version Entity Not Support updateBatch');
    }
    
    public function deleteBatch(array $ids){
    	throw new Exception('Version Entity Not Support deleteBatch');  	
    }
    
    public function merge($id = NULL){
    	$nd = $this->getAllData();
    	if(is_null($id)){
    		$id = $this->getVersionIdValue();
    	}
    	if(!empty($id)){
    		$this->load($id)->addData($nd);
    	}
    	return $this;
    }    

    public function load($id) {
        $data = $this->getQuery()
                     ->condition('v.'.$this->versionIdProperty, $id)
                     ->fetch()
                     ->getFirstRow();
        if(!is_null($data)){
            $this->fill($data);
        }
    	return $this;
    }
    
    public function loadBy(array $conditions){
        $q = $this->getQuery();
        foreach($conditions as $name=>$value){
            $prefix = array_key_exists($name, $this->versionProperties)? 'v': 'm';
            $q->condition($prefix.'.'.$name, $value);
        }
        $data = $q->fetch()->getFirstRow();
        if(!is_null($data)){
            $this->fill($data);
        }
        return $this;
    }    
}